/*
    This file is part of Posixcpp library.

    Posixcpp library is free software: you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public License
    as published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    Posixcpp library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU LesserGeneral Public
    License along with Posixcpp library. If not, see
    <http://www.gnu.org/licenses/>.
 */

#ifndef POSIX_SOCKETS_SOCKET_HPP
#define	POSIX_SOCKETS_SOCKET_HPP

#include "posix_io_sockets.hpp"
#include "posix_io_handle.hpp"

#include <linux/if_packet.h>
#include <net/ethernet.h>

namespace posix {
namespace io {
namespace sockets {

class Address {
public:
    virtual ~Address();
    virtual size_t length() const = 0;

    sockaddr* handle() {
        return m_address;
    }
    const sockaddr* handle() const {
        return m_address;
    }
    
    Family_t family () {
        return Family_t(m_address->sa_family);
    }
    
protected:
    Address(sockaddr* addr, Family_t family = Family_t::UNSPECIFIED);
private:
    sockaddr* m_address;
};

class PhisicalAddress final : public Address
{
public:
    PhisicalAddress(uint16_t ethernetProtocol = ETH_P_ALL);

    ~PhisicalAddress() {
    }

    void init(uint16_t ethernetProtocol = ETH_P_ALL);

    size_t length() const override;

    int ifIndex() const {
        return this->m_address.sll_ifindex;
    }

    void setIfIndex(int newVal) {
        this->m_address.sll_ifindex = newVal;
    }

    uint16_t protocol() const {
        return be16toh(this->m_address.sll_protocol);
    }

    void setProtocol(uint16_t newVal) {
        this->m_address.sll_protocol = htobe16(newVal);
    }

private:
    sockaddr_ll m_address;
};

class Socket : public io::Handle
{
public:
    Socket(Family_t domain, Type_t type, int protocol = 0);
    virtual ~Socket();
    
    void listen();
    Socket accept();
    void shutdown();

    Type_t type() const {
        return m_type;
    }

    void settype(Type_t newVal) {
        m_type = newVal;
    }

    void send(const posixcpp::ByteArray & data);
    void receive(posixcpp::ByteArray & data);

    size_t receiveBufferSize();
    void setReceiveBufferSize(size_t newVal);

    int protocol() const {
        return m_protocol;
    }

    void setProtocol(int newVal) {
        m_protocol = newVal;
    }

protected:
    Type_t m_type;
    Family_t m_domain;
    int m_protocol;
};

}
}
}

#endif	/* POSIX_SOCKETS_SOCKET_HPP */
